/*
 * LeafNodeSerializer_test.cpp
 *
 *  Created on: Nov 1, 2012
 *      Author: m
 */

#include "LeafNodeSerializer.h"
#include "BPlusTreeDefinitions.h"
#include "../../../physical_layer/physical_layer.h"

#include <memory>
#include <iostream>
#include <string>
#include <fstream>
#include <cstring>
#include <cstdlib>
#include <sstream>

#define _SERIALIZE_MODE_ARG   "-s"
#define _DESERIALIZE_MODE_ARG "-d"
#define _BIN_BUFFER_SIZE 1024*50

typedef enum
{
    serialize,
    deserialize,
    invalid
}
operation_mode_t;

std::string outfileName;
std::string infileName;
operation_mode_t mode;

void invalid_args()
{
    std::cerr << "Argumentos inválidos. "                       << std::endl
            << "Uso: lns_ts [-s|-d] infile outfile"             << std::endl
            << "Descripción: Serializa/Deserializa nodos hoja." << std::endl
            << "Modos:"                                         << std::endl
            << "\t-s: serialize"                                << std::endl
            << "\t-d: deserialize"                              << std::endl;
    exit( 1 );
}

void parse_args( int argc, char* argv[] )
{
    if ( strcmp( argv[1], _SERIALIZE_MODE_ARG ) == 0 )
        mode = serialize;
    else
        if ( strcmp( argv[1], _DESERIALIZE_MODE_ARG ) == 0 )
            mode = deserialize;
        else
            mode = invalid;

    infileName = argv[2];
    outfileName = argv[3];
}

void invalid_input_file_format()
{
    std::cerr << "Invalid input file format." << std::endl;
    exit( 2 );
}

int main( int argc, char* argv[] )
{
    std::ifstream infile;
    std::ofstream outfile;
    std::string lineBuffer;
    std::stringstream ss;
    unsigned int relPos;
    unsigned int reserved;
    std::string  strBuffer;
    size_t nodeId, nodeLvl, rightSibling;
    LeafNodeSerializer serializer;
    physical::byte binaryBuffer[_BIN_BUFFER_SIZE];
    size_t serializedBytes;
    std::shared_ptr< LeafRegister_t > lrp;
    std::shared_ptr< LeafSerializationBlock > lsbp;
    LeafRegisterList_ptr listp;
    int blockCounter;

    if ( argc < 4 )
        invalid_args();

    parse_args( argc, argv );

    switch ( mode )
    {
    case serialize:

        infile.open( infileName );
        outfile.open( outfileName, std::ios_base::binary | std::ios_base::trunc );

        // Tengo que "crear" LeafNodeSerialization blocks
        // levantando los datos del archivo de entrada
        // El formato es:
        // node  ::NODEID(uint)::
        // level ::NODELEVEL(uint)::
        // rightSibling ::SIBLINGID(uint)::
        // ::TERM(string):: ::RELPOS(uint):: ::RESERVED(int)::
        // ::TERM(string):: ::RELPOS(uint):: ::RESERVED(int)::
        // ::TERM(string):: ::RELPOS(uint):: ::RESERVED(int)::
        // ::TERM(string):: ::RELPOS(uint):: ::RESERVED(int)::

        while( getline( infile , lineBuffer) )
        {
            ss.clear();
            ss.str( lineBuffer );
            ss >> strBuffer;

            blockCounter = 0;

            // intento parsear header de nodo
            if ( strBuffer.compare("node") == 0 )
            {
                // Extraigo el node Id
                ss >> nodeId;

                // Intento leer el nivel
                getline( infile , lineBuffer);
                ss.clear();
                ss.str( lineBuffer );
                ss >> strBuffer;

                // Extraigo el node level
                if ( strBuffer.compare("level") == 0 && ss >> nodeLvl && getline( infile , lineBuffer) )
                {
                    // Intento leer el nodeId del hermano derecho
                    ss.clear();
                    ss.str( lineBuffer );

                        // Extraigo el nodeid del rightSibling
                    if ( ss >> strBuffer && strBuffer.compare("rightSibling") == 0 && ss >> rightSibling )
                    {
                        // serializo el nodo anterior ( si lo habia... )
                        if ( lsbp )
                        {
                            serializedBytes = serializer.serialize( lsbp, binaryBuffer);

                            // lo guardo en disco
                            memset( binaryBuffer+serializedBytes, 0xFF, _BIN_BUFFER_SIZE-serializedBytes );
                            outfile.write( binaryBuffer, _BIN_BUFFER_SIZE );
                        }

                        // creo un nuevo "nodo"
                        listp.reset( new LeafRegisterList_t );
                        lsbp.reset( new LeafSerializationBlock( { nodeLvl, rightSibling, listp } ) );
                        continue;
                    }
                    else
                        invalid_input_file_format();
                }
                else
                    invalid_input_file_format();
            }

            // sino asumo que es un registro
            // lo parseo y lo inserto en el nodo actual

            ss >> relPos;
            ss >> reserved;

            lrp.reset( new LeafRegister_t( strBuffer, { relPos, reserved } ) );

            // lo inserto en la lista de registros
            lsbp->content->push_back( lrp );
        }

        // serializo y grabo el último bloque
        if ( lsbp )
        {
            serializedBytes = serializer.serialize( lsbp, binaryBuffer);

            // lo guardo en disco
            memset( binaryBuffer+serializedBytes, 0xFF, _BIN_BUFFER_SIZE-serializedBytes );
            outfile.write( binaryBuffer, _BIN_BUFFER_SIZE );
        }

        break;

    case deserialize:

        infile.open( infileName, std::ios_base::binary );
        outfile.open( outfileName, std::ios_base::trunc );

        blockCounter = 1;

        while( infile.read( binaryBuffer, _BIN_BUFFER_SIZE ) )
        {
            serializer.deserialize( binaryBuffer, lsbp );

            // Dumpeo los atributos del blocke de nodo hoja
            // El formato es:
            // node  ::NODEID(uint)::
            // level ::NODELEVEL(uint)::
            // rightSibling ::SIBLINGID(uint)::
            // ::TERM(string):: ::RELPOS(uint):: ::RESERVED(int)::
            // ::TERM(string):: ::RELPOS(uint):: ::RESERVED(int)::
            // ::TERM(string):: ::RELPOS(uint):: ::RESERVED(int)::
            // ::TERM(string):: ::RELPOS(uint):: ::RESERVED(int)::

            outfile << "node "         << blockCounter       << std::endl;
            outfile << "level "        << lsbp->level        << std::endl;
            outfile << "rightSibling " << lsbp->rightSibling << std::endl;

            for ( LeafRegisterList_t::iterator it = lsbp->content->begin(); it != lsbp->content->end(); it++ )
            {

                outfile << (*it)->getKey()                    << " "
                        << (*it)->getValue().relativePosition << " "
                        << (*it)->getValue().reserved
                        << std::endl;
            }

            blockCounter++;
        }

        break;

    case invalid:
        invalid_args();
        break;
    }

    infile.close();
    outfile.close();

    return 0;
}
